package com.inspur.dgov.vlbod.secure.token;

import com.inspur.dgov.vlbod.secure.constant.SecureConstant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * Token创建工厂类
 * <pre>
 *     jwt的claim里一般包含以下几种数据:
 *         1. iss -- token的发行者
 *         2. sub -- 该JWT所面向的用户
 *         3. aud -- 接收该JWT的一方
 *         4. exp -- token的失效时间
 *         5. nbf -- 在此时间段之前,不会被处理
 *         6. iat -- jwt发布时间
 *         7. jti -- jwt唯一标识,防止重复使用
 * </pre>
 *
 * @Author chiyupei
 * @Date 2021/7/13 18:42
 * @Version 1.0
 * @Description 参数安全校验过滤工厂类
 */
public class TokenFactory {

    private static final String JWT_BASE64_ENCODED_SIGNING_KEY =
            Base64.getEncoder().encodeToString(SecureConstant.TOKEN_SIGNING_KEY.getBytes(StandardCharsets.UTF_8));

    /**
     * 利用JWT 生成 Token
     *
     * @param username 用户名
     * @param userId   用户id
     * @param map      附加内容
     * @return access token
     */
    public static String createAccessToken(String username, String userId, Map<String, Object> map) {
        Optional.ofNullable(username).orElseThrow(() -> new IllegalArgumentException("Cannot create Token without username"));

        Claims claims = Jwts.claims().setSubject(username)
                .setAudience(userId);
        claims.putAll(map);
        LocalDateTime currentTime = LocalDateTime.now();
        return Jwts.builder()
                .setClaims(claims)
                .setIssuer(SecureConstant.TOKEN_ISSUER)
                .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()))
                .setExpiration(Date.from(currentTime
                        .plusMinutes(SecureConstant.TOKEN_ACCESS_EXPTIME)
                        .atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(SignatureAlgorithm.HS512, JWT_BASE64_ENCODED_SIGNING_KEY)
                .compact();
    }

    /**
     * <pre>
     *  验证token是否失效
     *  true:过期   false:没过期
     * </pre>
     */
    public static Boolean isTokenExpired(String token) {
        try {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        } catch (ExpiredJwtException expiredJwtException) {
            return true;
        }
    }

    /**
     * 生成 刷新 RefreshToken
     *
     * @param username 用户名
     * @param userId   用户id
     * @param map      附加信息
     * @return refresh token
     */
    public static String createRefreshToken(String username, String userId, Map<String, Object> map) {
        Optional.ofNullable(username).orElseThrow(() -> new IllegalArgumentException("Cannot create Token without username"));
        LocalDateTime currentTime = LocalDateTime.now();
        Claims claims = Jwts.claims().setSubject(username).setAudience(userId);
        claims.putAll(map);
        return Jwts.builder()
                .setClaims(claims)
                .setIssuer(SecureConstant.TOKEN_ISSUER)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()))
                .setExpiration(Date.from(currentTime.plusMinutes(SecureConstant.TOKEN_REFRESH_EXPTIME).atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(SignatureAlgorithm.HS512, JWT_BASE64_ENCODED_SIGNING_KEY)
                .compact();
    }

    public static String getTokenSignature(String token) {
        if (null != token && !"".equals(token.trim())) {
            return "GOP_SIGN@" + token.split("\\.")[2];
        }
        return "";
    }

    /**
     * 从token中获取server
     *
     * @param token token
     * @return server
     */
    public static String getServerFromToken(String token) {
        return (String) getClaimFromToken(token).get("server");
    }

    /**
     * 获取jwt失效时间
     */
    public static Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token).getExpiration();
    }

    /**
     * 获取jwt的payload部分
     */
    public static Claims getClaimFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(JWT_BASE64_ENCODED_SIGNING_KEY)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 根据令牌获取用户信息
     *
     * @param token token
     * @return 用户信息
     */
    public static UserRecord getUserRecordFromToken(String token) {
        Claims claims = getClaimFromToken(token);
        UserRecord record = new UserRecord();
        record.setUserId(claims.getAudience());
        record.setUserName(claims.getSubject());
        record.setOrganCode(claims.get("organCode", String.class));
        record.setUserMobile(claims.get("userMobile", String.class));
        return record;
    }

}
